<?PHP
	/*
		Class: Model
		Database Model Class
	*/
	import("persistence.database.DataBase");
	class Model {
		// PRIVATE VARS
		var $_connection_uri;
		var $__database__;
		var $__table__;
		var $__record_vars__;
		var $__class_vars__;
		var $__framework_identifier__;
		var $_encryption = "md5";
		
		// PUBLIC VARS
		/*
			Property: id
			Database Unique identifier.
		*/
		var $id;
		/*
			Property: _lock_to_class_vars
			Lock database replication to defined class variables.
			
			Boolean - If *true*, only variables defined in the class definition will be filled with de data from the resultset.
						If *false* a variable will be created for each column the resultset has.
			
		*/
		var $_lock_to_class_vars = false;
		/*
			Property: _editables
			Define editables variables
			
			Array - If present, all the variables in the array can be edited and saved in the database.
					By default *all* variables are editables
		*/
		var $_editables;
		
		/* Property: _belongs_to 
			Define las clases a las que pertenece el active record
			Array - Nombres de las clases a las que pertenece
			Note: Debe contener una columna id_nombreClase para poder utilizarla
		*/
		var $_belongs_to = array();
		var $_has_many = array();
		var $_has_one = array();
		/*
		var $_db_text_types;
		var $_editables = array();
		var $_externals = array();
		var $_belongs_to = array();*/
		
		var $_order_by = FALSE;
		
		function Model($id=NULL,$orderBy=FALSE){
			if($orderBy) $this->_order_by = $orderBy;
			if($this->__table__==NULL) $this->__table__ = get_class($this)."s";
			if(DB_LOWERCASE_TABLES) $this->__table__ = strtolower($this->__table__);
			$this->__database__ = new DataBase();
			$this->id = $id;
			$this->__framework_identifier__ = "id_".strtolower(get_class($this));
			$this->_editables = array();
			if($this->id!=null){
				$additionalQuery = array();
				if($orderBy) $additionalQuery["order"] = $orderBy;
				$this->__get__($additionalQuery);
			}
		}
		function __buildVars__(){
			$this->__class_vars__ = array();
			$all_vars = ($this->_lock_to_class_vars) ? get_class_vars(get_class($this)) : get_object_vars($this);
			foreach($all_vars as $key => $value){
				if(
					$key[0]!="_" &&
					$key!="id" &&
					!is_object($value) &&
					!is_array($value) 
					/*&&
					$value!=""*/
					// TODO: Tener cuidado con los valores vacios, no me acuerdo porque se usaban
				){
					$this->__class_vars__[$key] = $value;
					//(APP_USE_HTMLENTITIES) ? htmlentities($value) : $value;
					//(APP_USE_HTMLENTITIES) ? htmlentities($rs->getData($fieldname)) : $rs->getData($fieldname);
				}
			}
			return $this->__class_vars__;
		}
		function __connect__(){
			$this->__database__->connect();
			//$this->__database__->selectDB(DB_SCHEMA);
		}
		
		function __query__($query){
			$this->__connect__();
			return $this->__database__->query($query);
		}
		
		function __execute__($query){
			$this->__connect__();
			return $this->__database__->execute($query);
		}
		
		function getOptions($optionString){
			$optionsArray = array();
			if(strLen($optionString)>0){
				$options = explode(";",$optionString);
				for($o=0;$o<count($options);$o++){
					$option = explode(":",$options[$o]);
					$optionsArray[$option[0]] = $option[1];//$this->processContent($option[1]);
				}
			}
			return $optionsArray;
		}
		
		function __get__($additionalQuery=array()){
			$query = new Query($this->__table__);
			$query->select = $this->__table__.".*";
			if(isset($this->id)){
				$query->where = $this->__table__.".id = ".$this->id;
			}else{ // if(!array_key_exists("where",$additionalQuery)){
				$classValues = $this->__buildVars__();
				$where = array();
				foreach($classValues as $key=>$value){
					$where[] = $this->__table__.".".$key."='".$value."'";
				}
				$query->where = join(" AND ",$where);
			}
			$query->order = $this->__table__.".id";
			if(is_array($additionalQuery)){
				foreach($additionalQuery as $key => $value){
					if($key!="order"){
						$property = strtolower($key);
						$query->$property = $value;
					}
				}
			}
			if(array_key_exists("order",$additionalQuery)){
				$query->order .= ", ".$additionalQuery["order"];
			}
			$sameNameCounter = 1;
			foreach($this->_belongs_to as $key=>$value){
				$options = (is_numeric($key)) ? array() : $this->getOptions($value);
				$className = (is_numeric($key))  ? $value : $key;
				$type = (array_key_exists("type", $options)) ? strToUpper($options["type"]) : "LEFT" ;
				///$className = (is_numeric($key)) ? $value : $key;
				//echo $className." - ".get_class($this)."<br/>";
				$tableas = (array_key_exists("as", $options)) ? $options["as"] : false;
				$field = (array_key_exists("field", $options)) ? $options["field"] : strToLower($className);
				
				$resolver = "";
				$as = "";
				
				if(class_exists($className)){
					$temp_model = new $className();
					$temp_table_name = strToLower($temp_model->__table__);//."s";
					unset($temp_model);
					if(strToLower($className)==strToLower(get_class($this))){
						$resolver = $sameNameCounter;
						$as = " AS ".$temp_table_name.$resolver;
						$sameNameCounter++;
					}
					if($tableas){
						$query->select .= ", ".$tableas.".*";
						$query->join[] = array(
							$type,
							$temp_table_name." AS ".$tableas,
							$tableas.".id = ".$this->__table__.".id_".$field
						  );
					}else{
						$query->select .= ", ".$temp_table_name.$resolver.".*";
						$query->join[] = array(
							$type,
							$temp_table_name.$as,
							$temp_table_name.$resolver.".id = ".$this->__table__.".id_".strToLower($className)
					  	);
					}
					$obj_name = strToLower($className);
				}else{
					trigger_error("Can't load Class [$className]",E_USER_WARNING);
				}
			}
			
			foreach($this->_has_one as $key=>$value){
				$options = (is_numeric($key)) ? array() : $this->getOptions($value);
				$className = (is_numeric($key))  ? $value : $key;
				$type = (array_key_exists("type", $options)) ? strToUpper($options["type"]) : "LEFT" ;
				///$className = (is_numeric($key)) ? $value : $key;
				//echo $className." - ".get_class($this)."<br/>";
				$resolver = "";
				$as = "";
				$temp_model = new $className();
				$temp_table_name = strToLower($temp_model->__table__);//."s";
				unset($temp_model);
				if(strToLower($className)==strToLower(get_class($this))){
					$resolver = $sameNameCounter;
					$as = " AS ".$temp_table_name.$resolver;
					$sameNameCounter++;
				}

				$query->select .= ", ".$temp_table_name.$resolver.".*";
				$query->join[] = array(
					$type,
					$temp_table_name.$as,
					//$temp_table_name.$resolver.".id = ".$this->__table__.".id_".strToLower($className)
					$temp_table_name.$resolver.".id_".strToLower($this->__class__)." = ".$this->__table__.".id".$where
				  );
				$obj_name = strToLower($className);
			}
			/*
			
					$options = (is_numeric($key)) ? array() : $this->getOptions($value);
					$className = (is_numeric($key))  ? $value : $key;
					$type = (array_key_exists("type", $options)) ? strToUpper($options["type"]) : "LEFT" ;*/
			foreach($this->_has_many as $key=>$value){
				$options = (is_numeric($key)) ? array() : $this->getOptions($value);
				$className = (is_numeric($key)) ? $value : $key;
				$type = (array_key_exists("type", $options)) ? strToUpper($options["type"]) : "LEFT" ;
				$link = (array_key_exists("link", $options)) ? $options["link"] : FALSE ;
				
				if(!$link){
					$temp_model = new $className();
					$temp_table_name = strToLower($temp_model->__table__);//."s";
					unset($temp_model);
					$query->select .= ", ".$temp_table_name.".*";
		
					$query->join[] = array(
							$type,
							$temp_table_name,
							$temp_table_name.".id_".strToLower(get_class($this)).
							"=".$this->__table__.".id"
						);
				}else{
					$temp_model = new $className();
					$temp_model_table_name = strToLower($temp_model->__table__);;
					unset($temp_model);
		
					$temp_table_name = strToLower($link);
					$query->select .= ", ".$temp_model_table_name.".*";

					$query->join[] = array(
							$type,
							$temp_table_name,
							$temp_table_name.".id_".strToLower(get_class($this)).
							"=".$this->__table__.".id"
						);

					$query->join[] = array(
							$type,
							$temp_model_table_name,
							$temp_model_table_name.".id = ".$temp_table_name.".id_".strToLower($className)
						);
				}
				/*if(is_numeric($key)){
					$className = $value;
					$temp_model = new $className();
					$temp_table_name = strToLower($temp_model->__table__);//."s";
					unset($temp_model);
					
//					$temp_table_name = strToLower($className."s");
					$query->select .= ", ".$temp_table_name.".*";
					
					$query->join[] = array(
							"LEFT",
							$temp_table_name,
							$temp_table_name.".id_".strToLower(get_class($this)).
							"=".$this->__table__.".id"
						);
				}else{
					$className = $key;
					
					$temp_model = new $className();
					$temp_model_table_name = strToLower($temp_model->__table__);//."s";
					unset($temp_model);
					
					$temp_table_name = strToLower($value);
					$query->select .= ", ".strToLower($temp_model_table_name).".*";//$className."s").".*";

					$query->join[] = array(
							"LEFT",
							$temp_table_name,
							$temp_table_name.".id_".strToLower(get_class($this)).
							"=".$this->__table__.".id"
						);

					$query->join[] = array(
							"LEFT",
							$temp_model_table_name,
							//strToLower($key)."s",
							//strToLower($key."s")
							$temp_model_table_name.".id = ".$temp_table_name.".id_".strToLower($key)
						);
				}*/
			}
			//$query->order = $this->_order_by;
			//echo $query->build();
			// DATABASE
			$res = $this->__query__($query);
			if($res){
				$res->moveCursor(0);
				$this->buildFromResultset($res);
				$sameNameCounter = 1;
				foreach($this->_belongs_to as $key=>$value){
					$className = (is_numeric($key)) ? $value : $key;
					$obj_name = strToLower($className);
					$resolver = "";
					
					$options = (is_numeric($key)) ? array() : $this->getOptions($value);
					$tableas = (array_key_exists("as", $options)) ? $options["as"] : FALSE;
					$field = (array_key_exists("field", $options)) ? $options["field"] : strToLower($className);
					if($obj_name==get_class($this)){
						$resolver = $sameNameCounter;
						$sameNameCounter++;
					}
					$this->$field = new $className();
					if($tableas){
						$this->$field->buildFromResultset($res,$tableas);
					}else{
						if($resolver!=""){
							$this->$field->buildFromResultset($res,$this->$obj_name->__table__.$resolver);
						}else{
							$this->$field->buildFromResultset($res);
						}
					}
				}					
				$res->beforeFirst();
				$old_ids = array();
				foreach($this->_has_many as $key=>$value){
					$property_name = (is_numeric($key)) ? strToLower($value."s") : strToLower($key."s");
					$this->$property_name = array();
					$old_ids[$property_name] = array();
				}
				while($res->next()){
					foreach($this->_has_many as $key=>$value){
						$property_name = (is_numeric($key)) ? strToLower($value."s") : strToLower($key."s");
						$class = (is_numeric($key)) ? $value : $key;
						$temp_obj = new  $class();
						$table = $temp_obj->__table__;
						$id = $res->getData($table.".id");
						if(array_search($id,$old_ids[$property_name])===FALSE || $id==NULL){
							$created = $temp_obj->buildFromResultset($res);
							if($created){
								array_push($this->$property_name, $temp_obj);
								array_push($old_ids[$property_name],$id);
							}
						}
					}
				}
			}
			return ($res!=false);
		}
		
		function buildFromResultset($rs,$astable=NULL){
			$table = ($astable!=NULL) ? $astable : $this->__table__;
			$temp_id = $rs->getData($table.".id");
			
			$hasValues = ($rs->getData($table.".id")!=NULL);			
			$this->id = $temp_id;
			$fields = $rs->getFields();
			for($f=0;$f<count($fields);$f++){
				$fieldname = $fields[$f];
				$completeName = explode(".",$fieldname);
				$realName = $completeName[1];
				$tableName = $completeName[0];
				if($tableName==""){
					$this->$realName = $rs->getData($fieldname);
					if($rs->getData($fieldname)!=NULL) $hasValues = TRUE;
				}else if($fieldname!=$table.".id" && $tableName==$table){
					$this->$realName = $rs->getData($fieldname);
					//(APP_USE_HTMLENTITIES) ? htmlentities($rs->getData($fieldname)) : $rs->getData($fieldname);
					if($rs->getData($fieldname)!=NULL) $hasValues = TRUE;
				} 
				//$this->__framework_identifier__
				
			}
			return $hasValues;
		}
		
		/*
			Function: save
			Guarda el objeto actual en la base de datos.
			Returns: Boolean
			True en caso de que el objeto se haya guardado correctamente.
		*/
		function save(){
			$result = false;
			if($this->id != null){
				$result = $this->__update__();
			}else{
				$result = $this->__insert__();  
			}
			return $result;
		}
		
		function __insert__(){
			$this->__buildVars__();
			$query = new Query($this->__table__,"insert");
			$query->keys = array_keys($this->__class_vars__);
			$query->values = $this->__class_vars__;
			$execute_result = $this->__execute__($query);
			$this->id = $this->__database__->getLastId();
			return $execute_result;
		}
		
		function __update__(){
			$this->__buildVars__();
			$query = new Query($this->__table__,"update");
			$query->keys = array_keys($this->__class_vars__);
			$query->values = $this->__class_vars__;
			$query->where = "id = ".$this->id;
			//echo $query->build();
			return $this->__execute__($query);
		}
		
		/*
			Function: delete
			Removes the respective row in the database.
			Returns: Boolean
			True if successfully removed.
		*/
		function delete(){
			$query = new Query($this->__table__,"delete");
			$query->where = "id = ".$this->id;
			return $this->__execute__($query);
		}
		function encryptValue($value){
			switch(strtolower($this->_encryption)){
			//	case "crypt"
				case "md5":
				default:
					$return = md5($value);
					break;
			}
			return $return;
		}
		function fill($data){
			$object_filled = FALSE;
			foreach($data as $key=>$value){
				$separator = strpos($key,"_");
				if($separator!==FALSE){
					$class = substr($key,0,$separator);
					$property = substr($key,$separator+1);
					$modseparator = strpos($property,":");
					$mod = "none";
					if($modseparator!==FALSE){
						$mod = substr($property,$modseparator+1);
						$property = substr($property,0,$modseparator);
					}
					if(strToLower($class)==strToLower(get_class($this))){
						$object_filled = TRUE;
						$propvalue = $value;
						switch(strtolower($mod)){
							case "date":
								$propvalue = $propvalue['year']."-".$propvalue['month']."-".$propvalue['day'];
								break;
							case "encrypt":
								$propvalue = $this->encryptValue($propvalue);
								break;
						}
						$this->$property = $propvalue;
					}
				}
				
			}
			return $object_filled;
		}
		
		/*
			Function: load
			Carga el objeto con los datos de la Base de Datos en base a $query

		   Parameters:

		      $query - Consulta (Query) con la que se quiere cargar el objeto.
		
			See Also:
				<Query>
		*/
		function load($query=array()){
			return $this->__get__($query);
		}
		
		function find($query=NULL){
			//$alist = new ActiveList("Client");
			//echo "pc".get_parent_class();
			//echo "c".get_class();
			//return get_class();
			//echo $query;
		}
	}

?>